import base64
import re
import ssl
import traceback
from datetime import datetime, timedelta
import random
import smtplib
from email.header import Header
from email.mime.text import MIMEText
import asyncio
import logging

import aiohttp
import jwt
import certifi
import aiomysql
from starlette import status

from http.client import HTTPException
from fastapi import HTTPException, Form
from fastapi import APIRouter

from pydantic import BaseModel
from fastapi import File, UploadFile

from apps.utils.sql_conn import get_db_cursor

certifi.core.where = lambda: '/path/to/cacert.pem'

auth = APIRouter()

API_KEY = "YcvHYS7NzCCzr3Cs0PjMqwzS"
SECRET_KEY = "mK6hUJSFpxaUdlOwV36JwOMaATnrtFF8"

JWT_SECRET_KEY = "5d7b3f2a4e9c1b6d8e7f3a9c2b5d6e4f9a7b8c3d2e5f6a7b9c8d7e6f5a4b3"
ALGORITHM = "HS256"


class FaceVerifyRequest(BaseModel):
    email: str
    similarity: float


class RealNameAuth(BaseModel):
    email: str
    file: UploadFile = File(...)


# 人脸参数
DB_IMAGE_PARAMS = {
    "image_type": "BASE64",
    "face_type": "LIVE",
    "quality_control": "NONE",
    "face_sort_type": 0
}

UPLOAD_IMAGE_PARAMS = {
    "image_type": "BASE64",
    "face_type": "LIVE",
    "quality_control": "HIGH",
    "liveness_control": "NORMAL",
    "face_sort_type": 0,
    "spoofing_control": "HIGH"
}


# 用户登录模型
class UserLogin(BaseModel):
    identifier: str
    password: str


# 用户注册模型
class UserRegister(BaseModel):
    email: str
    username: str
    password: str
    code: str


# 修改密码的模型
class UserChangePassword(BaseModel):
    email: str
    code: str
    new_password: str


host = "https://jumbarcode.market.alicloudapi.com"
path = "/tmcx/drug/query"
appcode = '97f15fedc05e4020ae11be8931545a5b'
url = host + path


class DrugQueryRequsest(BaseModel):
    key: str


async def delect_code_after_five_minutes(email, code):
    await asyncio.sleep(300)
    async with get_db_cursor() as cursor:
        try:
            query_delete = """
                DELETE FROM code WHERE code = %s AND acceptuser = %s
            """
            await cursor.execute(query_delete, (code, email))
        except aiomysql.Error as e:
            error_code = e.args[0]
            error_msg = e.args[1]
            detail = f"数据库错误: {error_msg} (错误代码: {error_code})"
            print(detail)


@auth.post("/auth/face_verify", response_description="人脸识别1：1接口")
async def face_verify(
        realname: str = Form(...),
        file: UploadFile = File(...)
):
    """
     人脸识别1:1比对流程：
    1. 接收上传的人脸图片并转为Base64
    2. 根据realname查询数据库获取idphoto图片链接
    3. 下载idphoto图片并转为Base64
    4. 调用百度人脸识别API进行1:1比对
    5. 若相似度>60，更新authentication状态为1
    """

    try:
        file_content = await file.read()
        upload_base64 = base64.b64encode(file_content).decode("utf-8")
    except Exception as e:
        raise HTTPException(
            status_code=400,
            detail=f"上传图片处理失败：{str(e)}"
        )

    async with get_db_cursor() as cursor:
        try:
            await cursor.execute(
                "SELECT id, idphoto, authentication FROM user_info WHERE realname = %s",
                (realname,)
            )
            user_data = await cursor.fetchone()
            if not user_data:
                raise HTTPException(
                    status_code=404,
                    detail="未找到匹配的用户信息"
                )

            if isinstance(user_data, dict):
                user_id = user_data["id"]
                idphoto_url = user_data["idphoto"]
                current_auth = user_data["authentication"]
            else:
                user_id = user_data[0]
                idphoto_url = user_data[1]
                current_auth = user_data[2]

            if not idphoto_url:
                raise HTTPException(
                    status_code=400,
                    detail="用户未上传身份证照片，无法进行比对"
                )

        except aiomysql.Error as e:
            raise HTTPException(
                status_code=500,
                detail=f"数据库查询失败：{e.args[1]}"
            )

            # 步骤3：下载idphoto图片并转为Base64

        async def download_image_as_base64(url):
            try:
                async with aiohttp.ClientSession() as session:
                    async with session.get(url) as resp:
                        if resp.status != 200:
                            raise Exception(f"图片下载失败，状态码：{resp.status}")
                        img_content = await resp.read()
                        return base64.b64encode(img_content).decode("utf-8")
            except Exception as e:
                raise HTTPException(
                    status_code=400,
                    detail=f"身份证照片处理失败：{str(e)}"
                )

        db_base64 = await download_image_as_base64(idphoto_url)

        # 步骤4：调用百度人脸识别API进行比对
        async def get_access_token():
            """复用获取百度API令牌的逻辑"""
            url = "https://aip.baidubce.com/oauth/2.0/token"
            params = {
                "grant_type": "client_credentials",
                "client_id": "ChQH7GlymZf1qiNJMrSGIgPU",  # 复用现有API_KEY
                "client_secret": "Zy4d6rNcIfWF1Xzbom3Ac0CTF8lzWmBq"  # 复用现有SECRET_KEY
            }
            async with aiohttp.ClientSession() as session:
                async with session.get(url, params=params) as resp:
                    data = await resp.json()
                    if "error" in data:
                        raise HTTPException(
                            status_code=400,
                            detail=f"获取令牌失败：{data['error_description']}"
                        )
                    return data["access_token"]

        async def compare_faces(token):
            url = "https://aip.baidubce.com/rest/2.0/face/v3/match"
            params = {"access_token": token}
            payload = [
                {
                    "image": db_base64,
                    "image_type": DB_IMAGE_PARAMS["image_type"],
                    "face_type": DB_IMAGE_PARAMS["face_type"],
                    "quality_control": DB_IMAGE_PARAMS["quality_control"],
                    "face_sort_type": DB_IMAGE_PARAMS["face_sort_type"]
                },
                {
                    "image": upload_base64,
                    "image_type": UPLOAD_IMAGE_PARAMS["image_type"],
                    "face_type": UPLOAD_IMAGE_PARAMS["face_type"],
                    "quality_control": UPLOAD_IMAGE_PARAMS["quality_control"],
                    "liveness_control": UPLOAD_IMAGE_PARAMS["liveness_control"],
                    "face_sort_type": UPLOAD_IMAGE_PARAMS["face_sort_type"],
                    "spoofing_control": UPLOAD_IMAGE_PARAMS["spoofing_control"]
                }
            ]
            ssl_context = ssl.create_default_context(cafile=certifi.where())
            async with aiohttp.ClientSession() as session:
                async with session.post(
                        url,
                        params=params,
                        json=payload,
                        headers={"Content-Type": "application/json"},
                        ssl=ssl_context

                ) as resp:
                    return await resp.json()

        try:
            token = await get_access_token()
            compare_result = await compare_faces(token)

            if compare_result["error_code"] != 0:
                raise HTTPException(
                    status_code=400,
                    detail=f"人脸识别失败：{compare_result['error_msg']}"
                )

            score = compare_result["result"]["score"]
            if score < 60:
                return {
                    "status": "failure",
                    "message": f"人脸匹配度不足（{score}分,识别非本人）",
                    "authentication": current_auth
                }

            async with get_db_cursor() as cursor:
                await cursor.execute(
                    "UPDATE user_info SET authentication = 1 WHERE id = %s",
                    (user_id,)
                )
                return {
                    "status": "success",
                    "message": f"人脸匹配成功（{score}分）",
                    "authentication": 1
                }

        except HTTPException as e:
            raise e
        except Exception as e:
            logging.error(f"人脸识别接口异常: {traceback.format_exc()}")
            raise HTTPException(
                status_code=500,
                detail=f"服务器内部错误：{str(e)}"
            )


@auth.post("/auth/realname_verify", response_description="身份证实名认证接口")
async def realname_verify(
        email: str = Form(...),
        file: UploadFile = File(...),
        phone: str = Form(...)
):
    """
       身份证实名认证流程：
       1. 接收上传的身份证图片文件，转为 Base64
       2. 调用百度 OCR 识别身份证信息
       3. 验证用户存在性（通过 email 查询 user_info）
       4. 匹配 idcard 表中的身份证信息
       5. 更新 user_info 表的实名认证状态
       """

    if not re.match(r'^1[3-9]\d{9}$', phone):
        raise HTTPException(
            status_code=400,
            detail="手机号格式不正确，请输入 11 位有效的手机号码"
        )

    try:

        file_content = await file.read()
        base64_img = base64.b64encode(file_content).decode("utf-8")
    except Exception as e:
        raise HTTPException(
            status_code=400,
            detail=f"文件处理失败：{str(e)}，请检查文件是否可正常读取"
        )

    async def get_access_token():
        url = "https://aip.baidubce.com/oauth/2.0/token"
        params = {
            "grant_type": "client_credentials",
            "client_id": API_KEY,
            "client_secret": SECRET_KEY
        }
        async with aiohttp.ClientSession() as session:
            async with session.get(url, params=params) as resp:
                token_data = await resp.json()
                if "error" in token_data:
                    raise HTTPException(
                        status_code=400,
                        detail=f"百度云认证失败: {token_data['error_description']}"
                    )
                return token_data["access_token"]

    async def recognize_idcard(token):
        url = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard"
        params = {"access_token": token}
        ocr_data = {
            "image": base64_img,
            "id_card_side": "front",  # 仅识别正面
            "detect_risk": "true",  # 开启风险检测
            "detect_quality": "true",  # 开启质量检测
            "detect_photo": "true",  # 检测是否为照片
            "detect_card": "true"  # 检测是否为身份证
        }
        ssl_context = ssl.create_default_context(cafile=certifi.where())

        async with aiohttp.ClientSession() as session:
            async with session.post(
                    url,
                    params=params,
                    data=ocr_data,
                    headers={"Content-Type": "application/x-www-form-urlencoded"},
                    ssl=ssl_context
            ) as resp:
                return await resp.json()

    try:
        access_token = await get_access_token()
        ocr_result = await recognize_idcard(access_token)

        if "error_code" in ocr_result:
            raise HTTPException(
                status_code=400,
                detail=f"OCR 识别失败: {ocr_result['error_msg']}（错误码：{ocr_result['error_code']}）"
            )

        words_result = ocr_result.get("words_result", {})
        realname = words_result.get("姓名", {}).get("words")
        id_card_num = words_result.get("公民身份号码", {}).get("words")

        if not realname or not id_card_num:
            raise HTTPException(
                status_code=400,
                detail="无法识别身份证信息，请确保图片清晰且包含姓名和身份证号"
            )

        async with get_db_cursor() as cursor:
            await cursor.execute(
                "SELECT id FROM user_info WHERE email = %s",
                (email,)
            )
            user = await cursor.fetchone()
            if not user:
                raise HTTPException(
                    status_code=404,
                    detail="用户不存在，请先注册"
                )

            await cursor.execute(
                "SELECT name, idcard, photo FROM idcard WHERE name = %s AND idcard = %s",
                (realname, id_card_num)
            )
            idcard_info = await cursor.fetchone()

            if not idcard_info:
                raise HTTPException(
                    status_code=400,
                    detail="身份证信息未在系统中登记，请先完成信息录入"
                )

            if isinstance(idcard_info, dict):
                name = idcard_info.get("name")
                idcard = idcard_info.get("idcard")
                photo = idcard_info.get("photo")
                if not all([name, idcard, photo]):
                    raise HTTPException(
                        status_code=400,
                        detail="身份证信息不完整，请检查登记信息"
                    )
            else:
                name = idcard_info[0]
                idcard = idcard_info[1]
                photo = idcard_info[2]

            await cursor.execute(
                """
                UPDATE user_info 
                SET realname = %s, identitycard = %s, idphoto = %s, verified = 1, phone = %s 
                WHERE email = %s
                """,
                (name, idcard, photo, phone, email)
            )

            return {
                "status": "success",
                "message": "实名认证成功",
                "data": {
                    "realname": name,
                    "identitycard": idcard,
                    "idphoto": photo,
                    "phone": phone
                },
                "verified": 1
            }

    except HTTPException as e:
        raise e
    except Exception as e:
        logging.error(f"实名认证接口异常: {traceback.format_exc()}")
        raise HTTPException(
            status_code=500,
            detail=f"服务器内部错误: {str(e)}"
        )


@auth.post("/auth/code", response_description="获取验证码")
async def email_send(email):
    sender = user = '2755708415@qq.com'  # 发送方的邮箱账号
    passwd = 'ulrbrkxwnzludcja'  # 授权码
    receiver = email  # 接收方的邮箱账号
    code = random.randint(100000, 999999)
    print(code)
    # HTML内容
    html_content = f"""   
        <head></head>
        <body>
        <h1 style="text-align: center">医盾通账号注册</h1>
        <p style="color:red;text-align: center">您的验证码为
        <u>{code}</u>，该验证码5分钟内有效，若非本人操作，请勿泄露。</p>
        </body> 
    """

    msg = MIMEText(html_content, 'html', 'utf-8')

    # 设置 From, To 和 Subject 标头
    nickname = '医盾通'
    encoded_nickname = f"{Header(nickname, 'utf-8').encode()} "  # 对非ASCII字符进行编码处理
    msg['From'] = f"{encoded_nickname}<2755708415@qq.com>"
    msg['To'] = receiver
    msg['Subject'] = Header('注册账号', 'utf-8')  # 使用 Header 来确保标题被正确编码

    try:
        # 建立 SMTP SSL 的连接，连接发送方的邮箱服务器
        smtp = smtplib.SMTP_SSL('smtp.qq.com', 465)
        # 登录发送方的邮箱账号
        smtp.login(user, passwd)
        # 发送邮件 发送方，接收方，发送的内容
        smtp.sendmail(sender, receiver, msg.as_string())
        # 关闭发送连接
        smtp.quit()

        async with get_db_cursor() as cursor:
            try:
                send_time = datetime.now()
                # 插入验证码
                query_insert = """
                    INSERT INTO code   (code,send_time,acceptuser)   VALUES (%s,%s,%s);
                """
                await cursor.execute(query_insert, (code, send_time, email))

                asyncio.create_task(delect_code_after_five_minutes(email, code))

                return code

            except aiomysql.Error as e:
                error_code = e.args[0]
                error_msg = e.args[1]

                # 处理特定错误
                if error_code == 1452:  # 外键约束失败
                    if e.args[1].find("parent_id") != -1:
                        detail = "回复的父评论不存在"
                    else:
                        detail = "用户或内容不存在"
                elif error_code == 1406:  # 数据过长
                    detail = f"输入数据过长: {error_msg}"
                else:
                    detail = f"数据库错误: {error_msg} (错误代码: {error_code})"
                logging.error(detail)
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=detail
                )

    except Exception as e:
        print(e)
        print('发送邮件失败')


@auth.post("/auth/login", response_description="用户登录", operation_id="unique_verify_user_auth_login")  # 登录成功查询接口
async def verify_user(user: UserLogin):
    async with get_db_cursor() as cursor:
        await cursor.execute(
            "SELECT * FROM user_info WHERE ( username = %s OR email = %s ) AND password = %s",
            (user.identifier, user.identifier, user.password)
        )
        user_data = await cursor.fetchone()

        if user_data:
            expire = datetime.utcnow() + timedelta(minutes=30)
            payload = {
                "sub": user.identifier,
                "exp": expire
            }
            token = jwt.encode(payload, JWT_SECRET_KEY, algorithm=ALGORITHM)
            await cursor.execute(
                "UPDATE user_info SET token = %s WHERE ( username = %s OR email = %s ) AND password = %s ",
                (token, user.identifier, user.identifier, user.password)
            )
            email = user_data.get("email") if isinstance(user_data, dict) else user_data[1]
            return {"message": "success", "token": token, "email": email}
        else:
            return {"message": "failure"}


@auth.post("/auth/register", response_description="插入用户信息")
async def user_register(user: UserRegister):
    async with get_db_cursor() as cursor:  # 游标在该代码块内有效
        # 1. 检查邮箱是否已注册
        await cursor.execute("SELECT * FROM user_info WHERE email = %s", (user.email,))
        existing_user = await cursor.fetchone()
        if existing_user:
            return {"message": "该邮箱已被注册，请使用其他邮箱"}

        # 2. 验证验证码（仍在代码块内，cursor有效）
        await cursor.execute("SELECT * FROM code WHERE code = %s AND acceptuser = %s", (user.code, user.email))
        code_verify = await cursor.fetchone()  # 正确：在游标关闭前执行

        # 3. 执行注册（仍在代码块内，cursor有效）
        if code_verify:
            await cursor.execute(
                "INSERT INTO user_info (email, username, password) VALUES (%s, %s, %s)",
                (user.email, user.username, user.password)
            )
            return {"message": "注册成功"}
        else:
            return {"message": "验证码错误或与注册邮箱不匹配"}


@auth.put("/auth/change_password", response_description="修改密码")
async def change_password(user: UserChangePassword):
    async with get_db_cursor() as cursor:
        await cursor.execute(
            "SELECT * FROM user_info WHERE email = %s",
            (user.email,)
        )
        existing_user = await cursor.fetchone()

        if not existing_user:
            return {"message": "该邮箱未注册，请先注册"}

        await cursor.execute(
            "SELECT * FROM code WHERE code = %s AND acceptuser = %s",
            (user.code, user.email)
        )
        code_verify = await cursor.fetchone()

        if not code_verify:
            return {"message": "验证码错误或与邮箱不匹配"}

        # 更新密码
        try:
            await cursor.execute(
                "UPDATE user_info SET password = %s WHERE email = %s",
                (user.new_password, user.email)
            )
            return {"message": "密码修改成功"}
        except aiomysql.Error as e:
            error_code = e.args[0]
            error_msg = e.args[1]
            detail = f"数据库错误: {error_msg} (错误代码: {error_code})"
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=detail
            )


@auth.get("/auth/check_idcard", response_description="检查身份证号是否实名")
async def check_idcard(idcard: str):
    async with get_db_cursor() as cursor:
        await cursor.execute(
            "SELECT * FROM user_info WHERE identitycard = %s",
            (idcard,)
        )
        user_data = await cursor.fetchone()
        if user_data:
            return 1
        else:
            return {"message": "未实名"}


@auth.get("/auth/information", response_description="根据邮箱查询用户信息")
async def information(email: str):
    async with get_db_cursor() as cursor:
        try:
            await cursor.execute(
                "SELECT email, realname, identitycard, phone FROM user_info WHERE email = %s",
                (email,)
            )
            user_data = await cursor.fetchone()
            if user_data:
                result = {
                    "email": user_data["email"],
                    "realname": user_data["realname"],
                    "identitycard": user_data["identitycard"],
                    "phone": user_data["phone"]
                }
                return result
            else:
                return {"message": "未找到对应邮箱的用户信息"}
        except aiomysql.Error as e:
            error_code = e.args[0]
            error_msg = e.args[1]
            detail = f"数据库查询失败：{error_msg} (错误代码: {error_code})"
            raise HTTPException(
                status_code=500,
                detail=detail
            )
